﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using DailyReport.Models;
using MailKit.Net.Smtp;
using MimeKit;
using MyHelpers;
using NPOI.HSSF.Record;

namespace DailyReport.Helpers
{
    /// <summary>
    /// 邮件收发的辅助类
    /// </summary>
    public class DrEmailHelper
    {
        private static readonly Regex _regEmail = new Regex("^\\s*([A-Za-z0-9_-]+(\\.\\w+)*@(\\w+\\.)+\\w{2,5})\\s*$",
            RegexOptions.Compiled);

        private static string _emailAddr = "dailyreportnet@163.com";
        private static string _emailCode = "chen112233";
        private static Dictionary<string, string> _dicMimeType;

        /// <summary>初始化Mime字典</summary>
        /// <returns></returns>
        private static bool IntiDic()
        {
            if (_dicMimeType != null) return true;

            try
            {
                _dicMimeType = new Dictionary<string, string>();
                string fileName = AppDomain.CurrentDomain.BaseDirectory.Trim() + "\\Helpers\\MimeTypes.txt";
                string[] lines = File.ReadAllLines(fileName, Encoding.ASCII);
                foreach (string line in lines)
                {
                    if (string.IsNullOrWhiteSpace(line)) continue;
                    var strs = line.Split('\t');
                    if (strs.Length == 4)
                    {
                        if (!_dicMimeType.ContainsKey(strs[0])) _dicMimeType.Add(strs[0], strs[1]);
                        if (!_dicMimeType.ContainsKey(strs[2])) _dicMimeType.Add(strs[2], strs[3]);
                    }
                    else if (strs.Length == 2)
                    {
                        if (!_dicMimeType.ContainsKey(strs[0])) _dicMimeType.Add(strs[0], strs[1]);
                    }
                }

                return true;
            }
            catch (Exception e)
            {
                LogHelper.WriteLog(nameof(IntiDic), e);
                _dicMimeType = null;
                return false;
            }
        } 

        /// <summary>发送邮件</summary>
        /// <param name="to">收件邮箱</param>
        /// <param name="subject">主题</param>
        /// <param name="body">正文</param>
        /// <param name="useSSL">是否采用SSL加盟</param>
        /// <returns>是否发送成功</returns>
        public static bool SendEmail(string to, string subject, string body, bool useSSL = true)
        {
            if (string.IsNullOrEmpty(to) || string.IsNullOrEmpty(subject) || string.IsNullOrEmpty(body))
                return false;
            if (!_regEmail.IsMatch(to))
                return false;

            try
            {
                var message = new MimeMessage();
                message.From.Add(new MailboxAddress("日志客户端", _emailAddr));
                message.To.Add(new MailboxAddress(to.Substring(0, to.IndexOf("@", StringComparison.Ordinal)), to));
                message.Subject = subject;

                message.Body = new TextPart("plain")
                {
                    Text = body
                };

                using (var client = new SmtpClient())
                {
                    // For demo-purposes, accept all SSL certificates (in case the server supports STARTTLS)
                    client.ServerCertificateValidationCallback = (s, c, h, ex) => true;

                    if (useSSL) client.Connect("smtp.163.com", 994, true);
                    else client.Connect("smtp.163.com", 25, false);

                    // Note: since we don't have an OAuth2 token, disable
                    // the XOAUTH2 authentication mechanism.
                    client.AuthenticationMechanisms.Remove("XOAUTH2");

                    // Note: only needed if the SMTP server requires authentication
                    client.Authenticate(_emailAddr, _emailCode);

                    client.Send(message);
                    client.Disconnect(true);
                }

                return true;
            }
            catch (Exception e)
            {
                LogHelper.WriteLog(nameof(SendEmail), e);
                return false;
            }
        }

        /// <summary>发送邮件（包含附件）</summary>
        /// <param name="to">收件邮箱</param>
        /// <param name="subject">主题</param>
        /// <param name="body">正文</param>
        /// <param name="listAttachPath">附件列表</param>
        /// <param name="useSSL">是否采用SSL加盟</param>
        /// <returns>是否发送成功</returns>
        public static bool SendEmailWithAttach(string to, string subject, string body, List<string> listAttachPath, bool useSSL = true)
        {
            if (!IntiDic()) return false;
            if (string.IsNullOrEmpty(to) || string.IsNullOrEmpty(subject) || string.IsNullOrEmpty(body))
                return false;
            if (!_regEmail.IsMatch(to))
                return false;
            if (listAttachPath == null || listAttachPath.Count == 0) return SendEmail(to, subject, body, useSSL);

            try
            {
                var message = new MimeMessage();
                message.From.Add(new MailboxAddress("日志客户端", _emailAddr));
                message.To.Add(new MailboxAddress(to.Substring(0, to.IndexOf("@", StringComparison.Ordinal)), to));
                message.Subject = subject;

                var bodyPart = new TextPart("plain")
                {
                    Text = body
                };

                // now create the multipart/mixed container to hold the message text and the
                // image attachment
                var multipart = new Multipart("mixed");
                multipart.Add(bodyPart);
                foreach (string path in listAttachPath)
                {
                    if (!File.Exists(path)) continue;

                    // create an image attachment for the file located at path
                    var ext = Path.GetExtension(path);
                    var mimePart = _dicMimeType.ContainsKey(ext) ? _dicMimeType[ext] : "application/octet-stream";
                    if (!mimePart.Contains("/")) continue;
                    var mimeParts = mimePart.Split('/');

                    var attachment = new MimePart(mimeParts[0], mimeParts[1])
                    {
                        ContentObject = new ContentObject(File.OpenRead(path), ContentEncoding.Default),
                        ContentDisposition = new ContentDisposition(ContentDisposition.Attachment),
                        ContentTransferEncoding = ContentEncoding.Base64,
                        FileName = Path.GetFileName(path)
                    };
                    multipart.Add(attachment);
                }

                // now set the multipart/mixed as the message body
                message.Body = multipart;

                using (var client = new SmtpClient())
                {
                    // For demo-purposes, accept all SSL certificates (in case the server supports STARTTLS)
                    client.ServerCertificateValidationCallback = (s, c, h, ex) => true;

                    if (useSSL) client.Connect("smtp.163.com", 994, true);
                    else client.Connect("smtp.163.com", 25, false);

                    // Note: since we don't have an OAuth2 token, disable
                    // the XOAUTH2 authentication mechanism.
                    client.AuthenticationMechanisms.Remove("XOAUTH2");

                    // Note: only needed if the SMTP server requires authentication
                    client.Authenticate(_emailAddr, _emailCode);

                    client.Send(message);
                    client.Disconnect(true);
                }

                return true;
            }
            catch (Exception e)
            {
                LogHelper.WriteLog(nameof(SendEmail), e);
                return false;
            }
        }

        /// <summary>向所有用户发送todo到期提醒</summary>
        /// <returns>是否成功（部分用户发送失败但程序整体无误时返回为true）</returns>
        public static bool SendTodoRemind()
        {
            try
            {
                var db = new DrDbHelper();
                var users = db.GetUserNames();
                if (users == null) return true;

                foreach (string userName in users)
                {
                    var email = db.GetEmail(userName);
                    if (string.IsNullOrWhiteSpace(email)) continue;

                    var list = db.GetOvertimeTodos(userName);
                    if (list == null) continue;

                    LogHelper.WriteLog("开始发送邮件", userName, list[0].Text);

                    StringBuilder sbBody = new StringBuilder(string.Format("{0}，您好！", userName));
                    sbBody.Append(Environment.NewLine);
                    sbBody.Append("\t您有下列todo项等待完成：");
                    sbBody.Append(Environment.NewLine);
                    sbBody.Append("----------------------------------------------------------------------");
                    sbBody.Append(Environment.NewLine);
                    int indexTodo = 1;
                    foreach (JsonTodo jtd in list)
                    {
                        sbBody.Append(string.Format("\t{0}\t{1}\t{2}", indexTodo, jtd.FinishDate, jtd.Text));
                        sbBody.Append(Environment.NewLine);
                        indexTodo++;
                    }
                    sbBody.Append("----------------------------------------------------------------------");
                    sbBody.Append(Environment.NewLine);
                    sbBody.Append("\t祝您工作愉快！");
                    SendEmail(email, "待办项todo提醒 - DailyReport", sbBody.ToString(), true);

                    LogHelper.WriteLog("结束发送邮件", userName, list[0].Text);
                }
                return true;
            }
            catch (Exception e)
            {
                LogHelper.WriteLog(nameof(SendTodoRemind), e);
                return false;
            }
        }

        /// <summary>向所有用户发送下星期计划编写提醒</summary>
        /// <returns>是否成功（部分用户发送失败但程序整体无误时返回为true）</returns>
        public static bool SendNextWeekPlanRemind()
        {
            try
            {
                var db = new DrDbHelper();
                var users = db.GetUserNames();
                if (users == null) return true;
                var nextMonday = db.GetMondayStr();

                foreach (string userName in users)
                {
                    var email = db.GetEmail(userName);
                    if (string.IsNullOrWhiteSpace(email)) continue;

                    var dt = db.GetWeekPlan(userName, nextMonday);
                    if (dt == null || dt.Rows.Count == 0) continue;
                    var dr = dt.Rows[0];
                    if (dr["TEXT"] is DBNull || string.IsNullOrWhiteSpace(dr["TEXT"].ToString()))
                        continue;

                    StringBuilder sbBody = new StringBuilder(string.Format("{0}，您好！", userName));
                    sbBody.Append(Environment.NewLine);
                    sbBody.Append("\t请于本周内完成下周工作计划！");
                    sbBody.Append(Environment.NewLine);
                    sbBody.Append(Environment.NewLine);
                    sbBody.Append("\t祝您工作愉快！");
                    sbBody.Append(Environment.NewLine);
                    SendEmail(email, "下周工作计划制定提醒 - DailyReport", sbBody.ToString(), true);
                }
                return true;
            }
            catch (Exception e)
            {
                LogHelper.WriteLog(nameof(SendTodoRemind), e);
                return false;
            }
        }
    }
}